第13节 Es6新特性

ES6介绍

阮一峰es6文档地址: https://es6.ruanyifeng.com/

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了也叫ECMAScript 2015。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言

(一) let 命令和const命令

知识点:

  1. let的用法类似于var,用于变量的声明

  2. 使用let就会产生块级作用域, let命令只在块级作用域内(也就是大括号内)有效

  3. let在同一作用域内只能声明一次, 不能重复声明

  4. let和const不存在变量提升

详细例子:

1. let命令

// 用let声明变量

<script>
   let username = '张十三';
   let age = 100; 
</script>

// 用let声明的变量有块级作用域

// es5没有块级作用域
{
    var i = 0;
}
console.log('i',i);


// es6有块级作用域
{
    // 用let声明的变量, 只在括号内有效
    let j = 1;
    console.log('{}',j);
}
 // 用let声明的变量,在括号外无效, 所以报错
console.log('j',j);

// 解决for循环问题

for (var i=0;i<5;i++) {
    // todo
}
console.log('i',i);

// 用let声明的变量j是局部变量
for(let j=0;j<5;j++) {
    setTimeout(function() {
        console.log(j);
    },1000)
}

2. const命令

  1. 用来声明常量
  2. 不能被重新赋值
  3. 若是对象, 可以修改属性, 但不能重新赋值
const PI = 3.141592653519383447;
// PI = 100;  // 会报错,因为常量不能被重新赋值

const obj = {
    username: 'zs',
    age:18
}
//obj = {};  // 会报错,不可以重新赋值
// 用const声明的对象可以修改属性
obj.username = 'lisi';
console.log(obj);
3.不能重复声明
var username = 'zs';
var username = 'lisi';

let age = 20;
// 报错 Identifier 'age' has already been declared
let age = 30; 

4. let和const声明不会提前

console.log(username); // es5:变量提前,值为undefined
console.log(age); // es6:变量声明不会提前, 所以报错
var username = "zs";  
let age = 20;

5.var、let、const 区别(背诵)

var 关键字的特点:
    -	变量(预解析)提升,可以在声明的前面使用
    - 同一作用域内可以多次声明同一个变量(即便这种方式不建议这样去写)
    - 能让它形成作用域的只有函数

let:声明变量的关键字
    - 没有变量提升,只能在声明之后使用
    - 同一作用域内只能声明一次
    - 你声明的变量的作用域仅限于最近的花括号内( {}const:只能用来声明常量,该常量一旦声明其值就不能改变(let的唯一区别 )
	  - 没有变量提升,只能在声明之后使用
    - 同一作用域内只能声明一次
    - 你声明的变量的作用域仅限于最近的花括号内( {}-const声明的变量: 不允许重新赋值,引用数据类型可以添加或修改属性

(二) 变量的解构赋值

知识点:

  1. 解构: 结构分解, 从一个整体的变量里分解出一部分来使用

  2. 数组解构

  3. 对象解构

  4. 函数参数解构和默认值

<script>  
//数组解构
let arr = ['a','b','c'];
// 对arr进行结构
let [x,y,z] = arr;
console.log(x);
console.log(y);
console.log(z);

//对象解构
let obj = {
    age: 100,
    username:'张三',
    addr: '广东'
} 
// 对象结构,对象是无序的,所以顺序无所谓
let {username,age,addr} = obj;
console.log(username);
</script>

// 2.在函数中使用解构
// 自动结构: let {name,age} = obj;
function say({name,age}) {
    console.log(name);
    console.log(age);
} 
let obj = {
    name: 'zs',
    age:18
} 
say(obj);

// 3.默认值(1)
function say(obj) {
  console.log(obj.name);
}
let obj = {
  name: "zs",
  age: 18,
};
// say(obj); // 正常操作
say(); // 因为没有传对象,所以obj的值为undefined, console.log(obj.name) 就相当于console.log(undefined.name); 会报错

// 对象设置默认值,obj默认为{} 
function say(obj={}) { 
  console.log(obj.name);
}
let obj = {
  name: "zs",
  age: 18,
}; 
say();

// 4.对象属性设置默认值(2)
function say({name='老胡',age=100}) { 
  console.log(name);
  console.log(age);
}
let obj = {
  name: "zs",
  age: 18,
}; 
say({}); // 必须传入{}, 如果不传,函数参数无法解构,会报错

// 5.对象和属性都设置默认值
function say({name='老胡',age=18}={}) { 
  console.log(name);
  console.log(age);
}
let obj = {
  name: "zs",
  age: 18,
}; 
say();

(三) 模板字符串

<!DOCTYPE html>
<html lang="en"> 
<body> 
    <script>
        let username = '老张';
        let age = 100;
        // let  str = '他叫'+username+',他老了';
        let str = `他叫${username},他${age}`;

        let htmlStr = `<ul>
                            <li>${username}</li>
                            <li>${age}</li> 
                        </ul>`;
 		console.log(htmlStr);
    </script>
</body>
</html>

(四) 对象的扩展

也就是对象的一些新特性

知识点:

  1. 属性和方法的简洁表示法

  2. 变量做属性名

  3. 合并对象

1.属性和方法的简洁表示法

let username = '张三';
let age = 20;
// es5对象的写法
const obj = {
    username: username,
    age: age,
    say: function() {
        console.log(this.username);
    }
}

// es6对象属性和对象的简洁表示方式
const obj2 = {
    // 如果属性和值相同,只写一个
    username,
    age,
    say() {
        console.log(this.username); 
    }
}

2.变量做属性名

let key = "username";

const obj = {
  // [key]使用变量
  [key]: "222",
  aaa: "333",
};
console.log(obj);

3.合并对象

let obj1 = {
    name: 'zs',
    age: 20
} 
let obj2 = {
    name: '李四',
    age: 20,
    addr: '广东深圳'
} 
const obj = Object.assign(obj1,obj2);
console.log(obj);

// 合并数组
let arr1 = [1,2,3];
let arr2 = [4,5,6];
let arr = arr1.concat(arr2);
console.log(arr);

(五) 函数的扩展: 箭头函数

知识点

  1. 箭头函数的常见表达方式

  2. 箭头函数的this指向

  3. 箭头函数不能做构造函数

  4. 箭头函数的arguments对象不可用

1. 箭头函数的常见表达方式

// 表达方式1

// 箭头函数
const add = (a,b)=> {
    return a+b;
}
add(2,3);

// 表达方式2: 箭头函数的简写方式

  1. 函数体只有一行的时候,可以简写成
  2. 上面的例子可以简写成下面的代码
const add = (a, b) => a + b;
add(2, 3);

const arr = [1, 2, 3, 41, 2, 34, 4, 12];
// es5写法
arr.filter(function (item) {
  return item > 20;
});
// 箭头函数的写法
arr.filter((item) => item > 20);

// 表达方式3: 参数只有一个

arr.filter(item => item > 20);

2. 箭头函数的this指向

  1. 箭头函数自己是没有this的, 它会捕获它的上下文(作用域), 作为它自己的this
  2. 从现象上看就是箭头函数的外面this指向谁, 它的this就指向谁
const obj = {
    say() {
        // this指向obj
        console.log('1',this);
        setTimeout(function(){
            // 匿名函数,this指向window
            console.log('2',this);
        },1000)

        setTimeout(()=>{
            // 匿名函数,this指向window
            console.log('3',this);
        },2000)
    }
} 
obj.say();

3. 箭头函数不能做构造函数

箭头函数本身没有this, 所以不能拿来做构造函数的

const Person = (name,age)=> {
    this.name = name;
    this.age = age;
}

const p = new Person();

4. 箭头函数的arguments对象

  1. 在浏览器环境下arguments对象不可用
  2. 在nodejs环境下, arguments对象可以用
 const say = () => {
    console.log('对象',arguments);
}
say(1,2,3);

(六) 数组的扩展

知识点:

  1. Array.from() // 将伪数组(类数组)变成真数组
  2. find() // 找到了就返回符合条件的成员, 找不着就返回undefined
  3. findIndex() // 找到了就返回符合条件的成员的下标, 找不着就返回-1
  4. includes() // 判断数组是否包含某个成员, 只能对数组成员是基本数据类型的数组使用
  5. some() // 判断数组是否包含某个成员
  6. keys() // 遍历键, 了解
  7. values() // 遍历值, 了解
  8. entries() // 遍历键值对, 了解

1. Array.from()

将伪数组(类数组)变成真数组

// itemList是一个伪数组,不是真的
let itemList = document.querySelectorAll('.item');
Array.isArray(itemList);  // false
let list = Array.from(Array);
Array.isArray(list);  // true

// arguments对象,函数调用时都会产生的一个对象, 用来存放所有实体参数
function say() { 
    console.log(Array.isArray(arguments)); //false arguments是个伪数组
    let args = Array.from(arguments);
    console.log(Array.isArray(args));  // true
} 
say('a','b','c');

2. find()

找到了就返回符合条件的成员, 找不着就返回undefined

let list = [
  { username: "zs", age: 21 },
  { username: "lisi", age: 20 },
  { username: "ww", age: 22 },
  { username: "lisi", age: 100 },
];

let res1 = list.find(function(item) {
    return item.username === 'lisi';
})
console.log('res1',res1);

let res2 = list.find(function(item) {
    return item.username === 'lisan';
})
console.log('res2',res2);

详细例子

// 1.Array.from
<script>
 
</script>

// 2.find
<script>
 
</script>

// 3.findIndex 
<script>
 
</script> 

(七) 扩展运算符 ...

知识点:

  1. 在对象中使用
  2. 在数组中使用
  3. 在函数中使用

1.在对象中使用

// 合并对象

const obj1 = {
    a:2,
    c:5
}
const obj2 = {
    a:3,
    b:4
} 
 
// 合并两个对象
const obj = {
    ...obj1,
    ...obj2, 
}
console.log(obj);

// 复制一个对象的同时修改或新增属性

const obj1 = {
    name: 'zs',
    age: 20
} 

const obj2 = {
    ...obj1,
    // 修改原来属性
    age: 30,
    // 新增属性
    addr: '深圳'
}
console.log(obj2);

2. 在数组中使用

const arr1 = [1,2,3];
const arr2 = [
    // 将arr1的所有成员放在这里
    ...arr1,
    2,
    4
]
console.log('arr2',arr2);

// 合并两个数组
const arr = [
    ...arr1,
    ...arr2
]
console.log('arr',arr)

3. 在函数中使用

const say = (a,b,...c)=> {
    console.log(a,b,c)
}
say(1,2,3,4,5,6,7);

(八) Promise(背诵)

知识点:

  1. promise定义
  2. promise使用步骤
  3. async await异步变同步
  4. promise是异步编程的一种解决方案, 可以用来解决回调地狱的问题

(1) 什么是promise

  1. promise中文意思承诺

  2. promise有三种状态:

    • pending 正在进行中
    • resolved 成功
    • rejected 失败
  3. 状态一旦改变,就无法再次改变状态,这也是它名字 promise-承诺 的由来,一个promise对象状态只能改变一次

  4. 我们可以使用promise来保存将来的数据

(2) promise的使用步骤

  1. 创建promise对象
  2. 存储成功或失败的数据
  3. 获取promise对象的数据

// promise例子

<!DOCTYPE html>
<html lang="en"> 
<body>
    <script type="module">
      let num = window.prompt("请输入数字");

      // 1.创建promise对象
      const obj = new Promise((resolve, reject) => {
        // num > 10定位成功, 否则失败
        if (num > 10) {
          // 2.成功时用resolve保存数据
          resolve({msg:'sucess',num});
        } else {
          // 3.失败时用reject保存数据
          reject({ msg: 'error', num });
        }
      });

      // 3.获取数据 then()获取成功的数据, catch()获取失败的数据
      obj.then((res)=> {
        console.log('成功', res);
      }).catch(err=> {
          console.log('失败',err);
      }) 
    </script>
</body> 
</html>

// axios和Promise的关系

<!DOCTYPE html>
<html lang="en">

<body>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.26.0/axios.min.js"></script>
    <script> 
      const xhr = axios.get('xxxxx');
      // axios.get()和axios.post() 都是返回一个Promise的实例对象
      console.log(xhr instanceof Promise);
      xhr.then(res=> {
          console.log(res);
      }).catch(err=> {
        console.log(err);
      }) 
    </script>
</body> 
</html>

(3) async await把异步变同步

01 一个小例子
<!DOCTYPE html>
<html lang="en"> 
<body>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.26.0/axios.min.js"></script>
    <script>
        const getData = async () => {
            try {
                const res = await axios.get('http://huruqing.cn:3003/category/all2');
                console.log(res.data);
            } catch (error) {
                console.log('错误信息',error);
            }
        }
        getData(); 
    </script>
</body> 
</html>
02 实际应用

promise是异步编程的一种解决方案, 是为了解决回调地狱的问题(核心在于async await)

编程题: 根据提供的三个接口, 编写代码,找到广东分校web02班何秀英,并打印她的年龄
http://huruqing.cn:3009/getSchool  获取学校列表,无需参数
http://huruqing.cn:3009/getClass  // 获取班级,需要schoolId
http://huruqing.cn:3009/getStudent  // 获取学生对象, 需要参数classId

// 使用回调的方式解决(以前的方式)

<!DOCTYPE html>
<html lang="en"> 
<body>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.26.0/axios.min.js"></script>
    <script>
        // 1.获取学校列表
        axios.get('http://huruqing.cn:3009/getSchool').then(res => {
            // schoolList 学校列表
            let schoolList = res.data;
            // 找到广东分校对应的编号
            let schoolObj = schoolList.find(item => item.schoolName === '广东分校');
            // 获取学校编号
            let schoolId = schoolObj.schoolId;
            // console.log('schoolId',schoolId);
            axios.get('http://huruqing.cn:3009/getClass?schoolId=' + schoolId).then(res => {
                let classList = res.data;
                let classObj = classList.find(item => item.className === 'web02');
                let classId = classObj.classId;
                axios.get('http://huruqing.cn:3009/getStudent?classId=' + classId).then(res => {
                    // 学生列表
                    let studentList = res.data; 
                    let studentObj = studentList.find(item => item.studentName === '何秀英');
                    console.log('何秀英的年龄是',studentObj.age);
                }).catch(err => {
                    console.log('网络请求失败')
                }); 
            }).catch(err => {
                console.log('网络请求失败');
            }); 
        }).catch(err => {
            console.log('网络请求失败');
        });
    </script>
</body> 
</html>

// 使用async await把异步变同步, 以上代码可以修如下

<!DOCTYPE html>
<html lang="en">

<body>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.26.0/axios.min.js"></script>
    <script>
        async function getData() {
            try {
                // 获取学校编号
                let schoolList = await axios.get('http://huruqing.cn:3009/getSchool');
                let schoolObj = schoolList.find(item => item.schoolName === '广东分校');
                let schoolId = schoolObj.schoolId;

                // 获取班级编号
                let classList = await axios.get('http://huruqing.cn:3009/getClass?schoolId=' + schoolId);
                let classObj = classList.find(item => item.className === 'web02');
                let classId = classObj.classId;

                // 获取学生年龄
                let studentList = await axios.get('http://huruqing.cn:3009/getStudent?classId=' + classId);
                let studentList = res.data;
                let studentObj = studentList.find(item => item.studentName === '何秀英');
                console.log('何秀英的年龄是', studentObj.age);
            } catch (error) {
                console.log('网络请求失败', error);
            }
        }
    </script>
</body> 
</html>

(九) ES6 模块系统

在html中使用模块

<script type="module"></script>

(1) 常用模块化规范

就是制作一个js模块要遵守的原则

  1. commonjs规范 nodejs遵守commonjs规范

  2. AMD规范 reqire.js遵守AMD规范

  3. CMD规范 sea.js遵守CMD规范(玉伯, 淘宝)

  4. ES6模块化规范

(2) ES6模块导出和导入

1. 导出和导入方式(一)

// login.js登录模块,通过export导出模块

export let username = '张三';
export let age = 20;

// index.html 登入模块, script标签需要添加 type="module"

<!DOCTYPE html>
<html lang="en"> 
<body> 
    <script type="module">
        // 通过import导入模块
        import {
            username,
            age
        } from "./login.js";
        console.log(username);
        console.log(age);
    </script>
</body> 
</html>
2. 导出和导入方式(二)
  • 导出通过export default
  • import xxx from 'xxx'

// login.js

export default {
  username:"张三",
  age:20,
};

// index.html

<!DOCTYPE html>
<html lang="en"> 
<body> 
    <script type="module"> 
        import obj from "./login.js"; 
        console.log(obj);
    </script>
</body> 
</html>

以上两种方式同时使用

// login.js

export let className = 'web1206';
export let teacher = '老胡';

export default {
  username:"张三",
  age:20,
};

// index.html

<!DOCTYPE html>
<html lang="en"> 
<body> 
    <script type="module"> 
        // 获取到login.js里export default导出的数据
        // import obj from "./login.js"; 
        // console.log(obj);

        // 获取到login.js里className和teacher的变量
        // import {className,teacher} from './login.js';
        // console.log(className,teacher);

        // 合并写法
        import obj,{className,teacher} from './login.js';
        console.log(obj);
        console.log(className,teacher);
    </script>
</body> 
</html>
3. 重命名

// login.js

export let className = 'web1206';
export let teacher = '老胡'; 
export let teacher2 = '老胡2'; 
export let teacher3 = '老胡3'; 
export let teacher4 = '老胡4'; 
export let teacher5 = '老胡5';  
export let teacher6 = '老胡6';  

// index.html

<!DOCTYPE html>
<html lang="en"> 
<body> 
    <script type="module">  
        // 使用as对变量进行重命名
        import {className as c,teacher as t} from './login.js';
        console.log(c,t); 

        // 把全部变量导入,保存在obj中
        import * as obj from './login.js'; 
        console.log(obj); 
    </script>
</body> 
</html>

(十) class

es5没有class的概念

  1. 声明一个类
  2. 类继承
// es6的类, 用来创建对象
class Person{
   // 构造器(其实就是es5里的构造函数)
   constructor(nation) {
    this.nation = nation;
    this.type = '人类';
   } 
   say() {
       console.log(`这是一个${this.nation}`);
   } 
   sing() {
       console.log('人类会唱歌');
   } 
} 
// 定义一个女人的类
class Woman extends Person{
    constructor(nation) {
        // 调用父类构造器
        super(nation);
        this.sex = '女人';
    }
}

var w = new Woman('中国');
console.log('国籍',w.nation);
console.log('类别',w.type);
w.sing();

(十一) 新类型(了解)

  1. map

  2. set

  3. symble 可以创建独一无二的值

// map类型
const arr = [['name','zs'],['age',100]];
const map = new Map(arr);
// 获取数据 
let name = map.get('name');
console.log(name);

// set
const arr = [1,2,3,4,2,1,56,2];
const set = new Set(arr); 
// 将set转成数组
const arr2 = [...set];
console.log('arr2',arr2);

// symble
let key = 'aaa';
let key2 = 'aaa'; 
console.log(key === key2);

// 希望key和key2不相等
let x = Symbol('aa');
let y = Symbol('aa');
console.log(x === y);

作业

  1. 把今天代码敲三遍(多多益善)
  2. 小程序